package com.yilin.platform.base_core.tools.captcha;

import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.io.resource.Resource;
import com.google.gson.reflect.TypeToken;
import com.yilin.platform.base_core.application.AppUtil;
import com.yilin.platform.base_core.commons.exception.BusinessException;
import com.yilin.platform.base_core.commons.utils.JsonUtil;
import com.yilin.platform.base_core.commons.utils.StringUtil;
import com.yilin.platform.base_core.commons.utils.VerifyImageUtil;
import com.yilin.platform.base_core.components.redis.RedisClient;
import com.yilin.platform.base_core.components.redis.RedisKeys;
import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class CaptchaUtil {

    //---------------------------------------------图形验证码----------------------------------------------------------

    public static CaptchaData generateCaptchaCode() {
        SpecCaptcha specCaptcha = new SpecCaptcha(100, 42, 4);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        specCaptcha.out(stream);
        byte[] bytes = Base64.encodeBase64(stream.toByteArray());
        String base64 = new String(bytes);
        String captchaImg = "data:image/gif;base64," + base64;
        CaptchaData captchaData = new CaptchaData();
        captchaData.setCaptchaId("captcha_data_" + AppUtil.getUUID());
        captchaData.setCaptchaImg(captchaImg);
        String lowerCase = specCaptcha.text().toLowerCase();
        Map<String, Object> map = new HashMap<>();
        map.put("captchaCode", lowerCase);
        map.put("saveTime", System.currentTimeMillis());
        String json = JsonUtil.objectToJson(map);
        RedisClient.getInstance().hset(RedisKeys.ACCOUNT_CAPTCHA_CODE_KEY, captchaData.getCaptchaId(), json);
        RedisClient.getInstance().expire(RedisKeys.ACCOUNT_CAPTCHA_CODE_KEY, 60 * 60 * 24);//一天后自动删除
        return captchaData;
    }

    public static boolean validateCaptchaCode(String id, String code) {
        if (id == null) return false;
        String vCodeJson = RedisClient.getInstance().hget(RedisKeys.ACCOUNT_CAPTCHA_CODE_KEY, id);
        if (vCodeJson == null) return false;
        Map<String, Object> map = JsonUtil.jsonToObject(vCodeJson, new TypeToken<Map<String, Object>>() {});
        Long saveTime = (Long) map.get("saveTime");
        long time = Long.parseLong(new BigDecimal(saveTime).toPlainString());
        long elapseTime = System.currentTimeMillis() - time;
        if (elapseTime / 1000 > 10 * 60) {
            removeCaptchaCode(id);
            throw new BusinessException("图片验证码已过期，请刷新页面");
        }
        String captchaCode = (String) map.get("captchaCode");
        return StringUtil.isNotEmpty(captchaCode) && captchaCode.toLowerCase().equals(code.toLowerCase());
    }

    public static void removeCaptchaCode(String id) {
        if (id == null) return;
        RedisClient.getInstance().hdel(RedisKeys.ACCOUNT_CAPTCHA_CODE_KEY, id);
    }

    // 获取所有保存的验证码KEY
    public static Map<String, Long> getAllCaptchaCodeMapTime() {
        Map<Object, Object> map = RedisClient.getInstance().hEntries(RedisKeys.ACCOUNT_CAPTCHA_CODE_KEY);
        Map<String, Long> accessMap = new HashMap<>();
        for (Object key : map.keySet()) {
            if (map.get(key) == null) continue;
            Map<String, Object> codeMap = JsonUtil.jsonToObject(map.get(key).toString(), new TypeToken<Map<String, Object>>() {});
            long aLong = (long) codeMap.get("saveTime");
            accessMap.put(key.toString(), aLong);
        }
        return accessMap;
    }


    //---------------------------------------------滑动验证码----------------------------------------------------------


    public static Map<String, Object> generateCaptchaSlide() {
        try {
            Map<String, Object> pictureMap = new HashMap<>();
            String captchaSlideId = "captcha_data_" + AppUtil.getUUID();
            int templateNum = new Random().nextInt(4) + 1;
            Resource resource = new ClassPathResource(String.format("classpath:validateImage/%s.png", templateNum));
            InputStream inputStream = resource.getStream();
//            File file = ResourceUtils.getFile(String.format("classpath:validateImage/%s.png", templateNum));
            VerifyImage img = VerifyImageUtil.getVerifyImage(inputStream);
            pictureMap.put("srcImage", img.getSrcImage());
            pictureMap.put("cutImage", img.getCutImage());
            pictureMap.put("yPosition", img.getYPosition());
            pictureMap.put("srcImageWidth", img.getSrcImageWidth());//高度前台自适应
            pictureMap.put("cutImageWidth", VerifyImageUtil.CUT_W_H);//高度前台自适应
            pictureMap.put("captchaSlideId", captchaSlideId);
            Map<String, Object> map = new HashMap<>();
            map.put("xPosition", img.getXPosition());
            map.put("saveTime", System.currentTimeMillis());
            String json = JsonUtil.objectToJson(map);
            RedisClient.getInstance().hset(RedisKeys.ACCOUNT_CAPTCHA_SLIDE_KEY, captchaSlideId, json);
            RedisClient.getInstance().expire(RedisKeys.ACCOUNT_CAPTCHA_SLIDE_KEY, 60 * 60 * 24);//一天后自动删除
            return pictureMap;
        } catch (Exception e) {
            throw new BusinessException("创建滑动验证失败:"+e.getMessage());
        }
    }

    public static boolean validateCaptchaSlide(String captchaSlideId, Integer xPosition) {
        if (captchaSlideId == null) return false;
        String vCodeJson = RedisClient.getInstance().hget(RedisKeys.ACCOUNT_CAPTCHA_SLIDE_KEY, captchaSlideId);
        if (vCodeJson == null) return false;
        Map<String, Object> map = JsonUtil.jsonToObject(vCodeJson, new TypeToken<Map<String, Object>>() {});
        long time = (Long) map.get("saveTime");
        long elapseTime = System.currentTimeMillis() - time;
        if (elapseTime / 1000 > 10 * 60) {
            removeCaptchaSlide(captchaSlideId);
            throw new BusinessException("滑动验证码已过期，请刷新页面");
        }
        String captchaSlide = map.get("xPosition").toString();
        Integer offset = Integer.valueOf(captchaSlide);
        if (xPosition >= offset && xPosition - offset > 10) return false;
        return xPosition >= offset || offset - xPosition <= 10;
    }

    public static void removeCaptchaSlide(String userName) {
        if (userName == null) return;
        RedisClient.getInstance().hdel(RedisKeys.ACCOUNT_CAPTCHA_SLIDE_KEY, userName);
    }

    // 获取所有保存的验证码KEY
    public static Map<String, Long> getAllCaptchaSlideMapTime() {
        Map<Object, Object> map = RedisClient.getInstance().hEntries(RedisKeys.ACCOUNT_CAPTCHA_SLIDE_KEY);
        Map<String, Long> accessMap = new HashMap<>();
        for (Object key : map.keySet()) {
            if (map.get(key) == null) continue;
            Map<String, Object> codeMap = JsonUtil.jsonToObject(map.get(key).toString(), new TypeToken<Map<String, Object>>() {});
            long aLong = (long) codeMap.get("saveTime");
            accessMap.put(key.toString(), aLong);
        }
        return accessMap;
    }
}
